#include "stdafx.h"
#include "include/Gs.h"
#include "../Context.h"
#include "../Value.h"
#include "../String.h"

namespace Gs
{
	// Gets the relative index to the top value on the stack.
	int Context::Top() const
	{
		return _stackTop - _stackFrame;
	}

	// Gets the value at the specified index in the stack.
	IValue *Context::ValueAt(int index) const
	{
		if (Top() == -1)
			throw IndexOutOfBoundsException("The stack contains no values.");
		int i = 0;
		if (index < 0)
			i = _stackTop + index + 1;
		else
			i = _stackFrame + index;
		if (i < 0 || i > _stackTop)
			throw IndexOutOfBoundsException();
		return _stack + i;
	}

	// Gets the value at the absolute index in the stack.
	Value *Context::ValueAtAbs(int index) const
	{
		return _stack + index;
	}

	// Pops the top value off the stack.
	void Context::Pop()
	{
		if (Top() == -1)
			throw InvalidOperationException("No elements on the stack to pop.");
		_stackTop--;
	}

	// Pushes a nil value onto the stack.
	void Context::Push()
	{
		CheckedPush()->Set();
	}

	// Pushes an integer onto the stack.
	void Context::Push(int i)
	{
		CheckedPush()->Set(i);
	}

	// Pushes a float onto the stack.
	void Context::Push(float f)
	{
		CheckedPush()->Set(f);
	}

	// Pushes a string onto the stack.
	void Context::Push(const char *s)
	{
		Push(CreateString(s));
	}

	// Pushes a string onto the stack.
	void Context::Push(IString *s)
	{
		CheckedPush()->Set((String *)s);
	}

	// Pushes an object onto the stack.
	void Context::Push(IObject *o)
	{
		CheckedPush()->Set(o);
	}

	// Pushes a native function onto the stack.
	void Context::Push(NativeFunction f)
	{
		if (!f)
			throw InvalidParameterException("f", "A valid native function pointer");
		Function *func = CreateFunction(f);
		CheckedPush()->Set(func);
	}

	// Pushes a user pointer onto the stack.
	void Context::Push(UserPointer up)
	{
		CheckedPush()->Set(up);
	}

	// Pushes a value onto the stack.
	void Context::Push(IValue *v)
	{
		if (v >= _stack && v < _stack + _stackSize)
		{
			Value val;
			val.Set((Value *)v);
			CheckedPush()->Set(&val);
		}
		else
			CheckedPush()->Set((Value *)v);
	}

	// Pushes the stack frame onto the stack.
	void Context::PushFrame()
	{
		CheckedPush()->Set(_stackFrame);
		_stackFrame = _stackTop;
	}

	// Pops the stack frame off the stack.
	void Context::PopFrame()
	{
		if (_stackTop == -1)
			throw InvalidOperationException("No elements on the stack to pop.");
		Value *v = ValueAtAbs(-1);
		if (v->GetType() == IntegerType)
		{
			_stackFrame = v->ToInt();
			Pop();
		}
	}
	
	// Gets the value at the newly checked and pushed index.
	Value *Context::CheckedPush()
	{
		if (_stackSize - _stackTop == 1)
		{
			int sz = _stackSize + MinimumStackGrowth;
			Value *newStack = new Value[sz];
			memcpy(newStack, _stack, _stackTop + 1);
			delete [] _stack;
			_stack = newStack;
			_stackSize = sz;
		}
		++_stackTop;
		return _stack + _stackTop;
	}
}